{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2024-03-11T14:19:47.710314500Z",
     "start_time": "2024-03-11T14:18:30.346241200Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "from itertools import chain\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch\n",
    "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from torch import nn\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "# 定义使用的设备GPU or CPU\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "# 数据集所在路径（相对）\n",
    "root_dir = \"data\"\n",
    "# 数据集文件名称\n",
    "file_name = \"F10.7.csv\"\n",
    "# dataLoader的batch_size\n",
    "batch_size = 4\n",
    "# LSTM进行预测时使用的过去数据的数量\n",
    "# 查阅论文得知\n",
    "seq_len = 30\n",
    "# 保存的模型位置\n",
    "BILSTM_ATTENTION_PATH = \"bilstm_attention.pth\"\n",
    "# tensorBoard对象\n",
    "writer = SummaryWriter(\"logs\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [],
   "source": [
    "# 使用pandas获取数据集文件对象\n",
    "data_file = pd.read_csv(os.path.join(root_dir, file_name))\n",
    "# 读取F10.7指数列\n",
    "F107_data = data_file['F10.7'].values.astype(float)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:39.119413600Z",
     "start_time": "2024-01-03T05:55:39.061401100Z"
    }
   },
   "id": "b32d17a287fecce7"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mKeyboardInterrupt\u001B[0m                         Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[7], line 2\u001B[0m\n\u001B[0;32m      1\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m i, data \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28menumerate\u001B[39m(F107_data):\n\u001B[1;32m----> 2\u001B[0m     \u001B[43mwriter\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43madd_scalar\u001B[49m\u001B[43m(\u001B[49m\u001B[43mtag\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mF10.7\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mscalar_value\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mdata\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mglobal_step\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mi\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\site-packages\\torch\\utils\\tensorboard\\writer.py:391\u001B[0m, in \u001B[0;36mSummaryWriter.add_scalar\u001B[1;34m(self, tag, scalar_value, global_step, walltime, new_style, double_precision)\u001B[0m\n\u001B[0;32m    386\u001B[0m     scalar_value \u001B[38;5;241m=\u001B[39m workspace\u001B[38;5;241m.\u001B[39mFetchBlob(scalar_value)\n\u001B[0;32m    388\u001B[0m summary \u001B[38;5;241m=\u001B[39m scalar(\n\u001B[0;32m    389\u001B[0m     tag, scalar_value, new_style\u001B[38;5;241m=\u001B[39mnew_style, double_precision\u001B[38;5;241m=\u001B[39mdouble_precision\n\u001B[0;32m    390\u001B[0m )\n\u001B[1;32m--> 391\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_get_file_writer\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43madd_summary\u001B[49m\u001B[43m(\u001B[49m\u001B[43msummary\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mglobal_step\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mwalltime\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\site-packages\\torch\\utils\\tensorboard\\writer.py:113\u001B[0m, in \u001B[0;36mFileWriter.add_summary\u001B[1;34m(self, summary, global_step, walltime)\u001B[0m\n\u001B[0;32m    101\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Adds a `Summary` protocol buffer to the event file.\u001B[39;00m\n\u001B[0;32m    102\u001B[0m \u001B[38;5;124;03mThis method wraps the provided summary in an `Event` protocol buffer\u001B[39;00m\n\u001B[0;32m    103\u001B[0m \u001B[38;5;124;03mand adds it to the event file.\u001B[39;00m\n\u001B[1;32m   (...)\u001B[0m\n\u001B[0;32m    110\u001B[0m \u001B[38;5;124;03m    walltime (from time.time()) seconds after epoch\u001B[39;00m\n\u001B[0;32m    111\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m    112\u001B[0m event \u001B[38;5;241m=\u001B[39m event_pb2\u001B[38;5;241m.\u001B[39mEvent(summary\u001B[38;5;241m=\u001B[39msummary)\n\u001B[1;32m--> 113\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43madd_event\u001B[49m\u001B[43m(\u001B[49m\u001B[43mevent\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mglobal_step\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mwalltime\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\site-packages\\torch\\utils\\tensorboard\\writer.py:98\u001B[0m, in \u001B[0;36mFileWriter.add_event\u001B[1;34m(self, event, step, walltime)\u001B[0m\n\u001B[0;32m     94\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m step \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m     95\u001B[0m     \u001B[38;5;66;03m# Make sure step is converted from numpy or other formats\u001B[39;00m\n\u001B[0;32m     96\u001B[0m     \u001B[38;5;66;03m# since protobuf might not convert depending on version\u001B[39;00m\n\u001B[0;32m     97\u001B[0m     event\u001B[38;5;241m.\u001B[39mstep \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mint\u001B[39m(step)\n\u001B[1;32m---> 98\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mevent_writer\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43madd_event\u001B[49m\u001B[43m(\u001B[49m\u001B[43mevent\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\site-packages\\tensorboard\\summary\\writer\\event_file_writer.py:117\u001B[0m, in \u001B[0;36mEventFileWriter.add_event\u001B[1;34m(self, event)\u001B[0m\n\u001B[0;32m    112\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(event, event_pb2\u001B[38;5;241m.\u001B[39mEvent):\n\u001B[0;32m    113\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mTypeError\u001B[39;00m(\n\u001B[0;32m    114\u001B[0m         \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mExpected an event_pb2.Event proto, \u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m    115\u001B[0m         \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m but got \u001B[39m\u001B[38;5;132;01m%s\u001B[39;00m\u001B[38;5;124m\"\u001B[39m \u001B[38;5;241m%\u001B[39m \u001B[38;5;28mtype\u001B[39m(event)\n\u001B[0;32m    116\u001B[0m     )\n\u001B[1;32m--> 117\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_async_writer\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mwrite\u001B[49m\u001B[43m(\u001B[49m\u001B[43mevent\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mSerializeToString\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\site-packages\\tensorboard\\summary\\writer\\event_file_writer.py:174\u001B[0m, in \u001B[0;36m_AsyncWriter.write\u001B[1;34m(self, bytestring)\u001B[0m\n\u001B[0;32m    172\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_closed:\n\u001B[0;32m    173\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mIOError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mWriter is closed\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m--> 174\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_byte_queue\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mput\u001B[49m\u001B[43m(\u001B[49m\u001B[43mbytestring\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m    175\u001B[0m \u001B[38;5;66;03m# Check the status again in case the background worker thread has\u001B[39;00m\n\u001B[0;32m    176\u001B[0m \u001B[38;5;66;03m# failed in the meantime to avoid waiting until the next call to\u001B[39;00m\n\u001B[0;32m    177\u001B[0m \u001B[38;5;66;03m# surface the error.\u001B[39;00m\n\u001B[0;32m    178\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_check_worker_status()\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\queue.py:139\u001B[0m, in \u001B[0;36mQueue.put\u001B[1;34m(self, item, block, timeout)\u001B[0m\n\u001B[0;32m    137\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m timeout \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m    138\u001B[0m     \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_qsize() \u001B[38;5;241m>\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mmaxsize:\n\u001B[1;32m--> 139\u001B[0m         \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mnot_full\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mwait\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m    140\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m timeout \u001B[38;5;241m<\u001B[39m \u001B[38;5;241m0\u001B[39m:\n\u001B[0;32m    141\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mtimeout\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124m must be a non-negative number\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n",
      "File \u001B[1;32mD:\\Anaconda3\\envs\\pytorch\\lib\\threading.py:302\u001B[0m, in \u001B[0;36mCondition.wait\u001B[1;34m(self, timeout)\u001B[0m\n\u001B[0;32m    300\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:    \u001B[38;5;66;03m# restore state no matter what (e.g., KeyboardInterrupt)\u001B[39;00m\n\u001B[0;32m    301\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m timeout \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[1;32m--> 302\u001B[0m         \u001B[43mwaiter\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43macquire\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m    303\u001B[0m         gotit \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mTrue\u001B[39;00m\n\u001B[0;32m    304\u001B[0m     \u001B[38;5;28;01melse\u001B[39;00m:\n",
      "\u001B[1;31mKeyboardInterrupt\u001B[0m: "
     ]
    }
   ],
   "source": [
    "for i, data in enumerate(F107_data):\n",
    "    writer.add_scalar(tag=\"F10.7\", scalar_value=data, global_step=i)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:34.445837700Z",
     "start_time": "2024-01-03T05:55:27.185612700Z"
    }
   },
   "id": "599f77fbe561d54f"
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [],
   "source": [
    "# 实现数据归一化\n",
    "def get_norm_dataLoader(datalist):\n",
    "    norm_scaler = MinMaxScaler(feature_range=(-1, 1))\n",
    "    # 使用sklearn中MinMaxScaler将训练集数据归一化至[-1,1]区间，为了获得更好的训练效果\n",
    "    norm_data = norm_scaler.fit_transform(datalist.reshape(-1, 1))\n",
    "    # 转换成Tensor类型\n",
    "    norm_data = torch.FloatTensor(norm_data).view(-1)\n",
    "    return norm_data\n",
    "\n",
    "\n",
    "# 生成训练序列，格式为（[（前27个数据），（预测的标签）...]）\n",
    "def generate_sequences(input_data, train_window):\n",
    "    out_seq = []\n",
    "    L = len(input_data)\n",
    "    for j in range(L - train_window):  #从第0项到倒数第27项\n",
    "        pre_seq = input_data[j:j + train_window]  # 前27项\n",
    "        train_label = input_data[j + train_window:j + train_window + 1]  # 第28项\n",
    "        out_seq.append((pre_seq, train_label))\n",
    "    return out_seq"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:41.412292800Z",
     "start_time": "2024-01-03T05:55:41.392288800Z"
    }
   },
   "id": "f882448192a3d5fc"
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "outputs": [],
   "source": [
    "# 创建自己的Dataset类\n",
    "class MyDataset(Dataset):\n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "\n",
    "    def __getitem__(self, item):\n",
    "        return self.data[item]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.data)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:41.968268500Z",
     "start_time": "2024-01-03T05:55:41.948271500Z"
    }
   },
   "id": "b81dead3f2c8bf4a"
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_seq:  tensor([0.2837, 0.1833, 0.2649, 0.0885, 0.1387, 0.1701, 0.1896, 0.2417, 0.3296,\n",
      "        0.3296, 0.3490, 0.3867, 0.3679, 0.4181, 0.3440, 0.4200, 0.3126, 0.4520,\n",
      "        0.4325, 0.5085, 0.3760, 0.3271, 0.4601, 0.3647, 0.2323, 0.2762, 0.4664,\n",
      "        0.6372, 0.7571, 0.7702])\n",
      "label: tensor([0.6051])\n"
     ]
    }
   ],
   "source": [
    "# 结合论文，按照太阳周期进行划分\n",
    "# 1957-1989年的数据作为训练集\n",
    "raw_train_data = F107_data[0:11380]\n",
    "train_data = get_norm_dataLoader(raw_train_data)\n",
    "\n",
    "# 1989-2000年的数据作为验证集\n",
    "raw_valid_data = F107_data[11380:15392]\n",
    "valid_data = get_norm_dataLoader(raw_valid_data)\n",
    "\n",
    "# 2000-2022年的数据作为测试集\n",
    "raw_test_data = F107_data[15392:23412]\n",
    "test_data = get_norm_dataLoader(raw_test_data)\n",
    "\n",
    "# 生成训练序列\n",
    "train_dataseq = generate_sequences(train_data, seq_len)\n",
    "# 生成Dataset对象 \n",
    "train_dataset = MyDataset(train_dataseq)\n",
    "# 利用DataLoader进行模型数据的输入\n",
    "train_dataloader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)\n",
    "for seq, label in train_dataseq:\n",
    "    print('train_seq: ', seq)\n",
    "    print('label:', label)\n",
    "    break\n",
    "\n",
    "# 生成验证序列\n",
    "valid_dataseq = generate_sequences(valid_data, seq_len)\n",
    "# 生成Dataset对象 \n",
    "valid_dataset = MyDataset(valid_dataseq)\n",
    "# 利用DataLoader进行模型数据的输入\n",
    "valid_dataloader = DataLoader(dataset=valid_dataset, batch_size=batch_size, shuffle=True, drop_last=True)\n",
    "\n",
    "# 生成测试序列\n",
    "test_dataseq = generate_sequences(test_data, seq_len)\n",
    "# 生成Dataset对象\n",
    "test_dataset = MyDataset(test_dataseq)\n",
    "# 利用DataLoader进行模型数据的输入\n",
    "test_dataloader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False, drop_last=True)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:42.882556900Z",
     "start_time": "2024-01-03T05:55:42.515245100Z"
    }
   },
   "id": "adc43ca58984c8f6"
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "outputs": [],
   "source": [
    "# 定义BiLSTM_Attention模型\n",
    "class BiLSTM_Attention(nn.Module):\n",
    "    def __init__(self, input_size=1, hidden_size=64, num_layers=5, output_size=1):\n",
    "        super().__init__()\n",
    "\n",
    "        self.input_size = input_size  # 必须，代表输入数据的形状，单变量所以为1\n",
    "        self.hidden_size = hidden_size  # 必须，设置的隐藏层节点个数，可以随意设置\n",
    "        self.num_layers = num_layers  # 必须，LSTM堆叠的层数，默认值是1层\n",
    "        self.output_size = output_size  # 最后输出的数据的形状\n",
    "        self.num_directions = 2  # 单向LSTM为1，双向LSTM则为2\n",
    "        self.batch_size = batch_size  # 使用了的DataLoader设置的batch_size\n",
    "\n",
    "        # 定义LSTM层\n",
    "        self.lstm = nn.LSTM(input_size=input_size, hidden_size=hidden_size, \n",
    "                            num_layers=num_layers,\n",
    "                            batch_first=True, bidirectional=True, dropout=0.3)\n",
    "        # 定义batch_first=True，可以使input和output的形状中batch_size提前\n",
    "\n",
    "        # 定义注意力机制\n",
    "        self.tanh1 = nn.Tanh()\n",
    "        self.w = nn.Parameter(torch.Tensor(hidden_size * 2, 1))\n",
    "        self.tanh2 = nn.Tanh()\n",
    "        # 从均匀分布U(a,b)中生成值,初始化W参数\n",
    "        nn.init.uniform_(self.w, -0.1, 0.1)\n",
    "\n",
    "        # 定义全连接层\n",
    "        self.linear = nn.Linear(hidden_size * 2, output_size)\n",
    "\n",
    "    def forward(self, input_seq):\n",
    "        # input_seq.shape : [8,27]\n",
    "        seq_loader_len = input_seq.shape[1]\n",
    "        # 27，用来预测的前面的数据数量\n",
    "\n",
    "        # 因为使用了DataLoader和设置了batch_first=True，所以要求输入的数据形式为\n",
    "        # input(batch_size, seq_len, input_size)，所以对输入的序列进行形状转换\n",
    "        input_seq = input_seq.view(self.batch_size, seq_loader_len, self.input_size)\n",
    "        # [8,27,1]\n",
    "\n",
    "        # 在前向传播的过程中，每次都随机初始化隐藏层参数，同时学习过程中也不进行隐藏层参数的学习\n",
    "        # h_0(num_directions * num_layers, batch_size, hidden_size)\n",
    "        # c_0(num_directions * num_layers, batch_size, hidden_size)\n",
    "        h0 = torch.randn(self.num_directions * self.num_layers, self.batch_size, self.hidden_size).to(device)\n",
    "        c0 = torch.randn(self.num_directions * self.num_layers, self.batch_size, self.hidden_size).to(device)\n",
    "\n",
    "        # 传入LSTM层，获得输出，并准备传入全连接层\n",
    "        # LSTM层的输入格式为 input, (h_0, c_0) ，输出为output, (h_n, c_n)\n",
    "        # 其中 input为 input(batch_size, seq_len, input_size)\n",
    "        # output为 output(batch_size, seq_len, num_directions * hidden_size)\n",
    "        lstm_out, _ = self.lstm(input_seq, (h0, c0))\n",
    "        # [8,27,128]\n",
    "\n",
    "        # 接下来是实现注意力机制\n",
    "        M = self.tanh1(lstm_out)\n",
    "        # [batch_size, seq_len, hidden_size * 2]\n",
    "        alpha = nn.functional.softmax(torch.matmul(M, self.w), dim=1)\n",
    "        # 此处w会被广播成为[batch_size, hidden_size * 2, 1]\n",
    "        # a = [batch_size, seq_len, 1]\n",
    "        out = lstm_out * alpha\n",
    "        # 此处进行hadamard积，仍旧借助广播机制\n",
    "        # out = [batch_size, seq_len, hidden_size * 2]\n",
    "        out = torch.sum(out, 1)\n",
    "        # [batch_size,hidden_size * 2]  \n",
    "        out = self.tanh2(out)\n",
    "\n",
    "        # 传入全连接层\n",
    "        out = self.linear(out)\n",
    "        # [batch_size,output_size]\n",
    "        return out"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:48.446251600Z",
     "start_time": "2024-01-03T05:55:48.432243100Z"
    }
   },
   "id": "7fc07fa6f4b3ab55"
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [],
   "source": [
    "# 定义模型\n",
    "model = BiLSTM_Attention()\n",
    "model = model.to(device)\n",
    "# 定义损失函数\n",
    "loss_fn = nn.MSELoss()\n",
    "loss_fn = loss_fn.to(device)\n",
    "# 定义优化器\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "# 定义训练轮数\n",
    "epochs = 40"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:53.407291400Z",
     "start_time": "2024-01-03T05:55:48.779326300Z"
    }
   },
   "id": "ef0309d7df444f92"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "train_all_loss = []\n",
    "valid_all_loss = []\n",
    "for epoch in range(epochs):\n",
    "    # 训练步骤开始\n",
    "    model.train()\n",
    "    train_loss = None\n",
    "    train_iter = 0\n",
    "    train_epoch_loss = 0\n",
    "    for train_iter, (train_seq, y_train) in enumerate(train_dataloader):\n",
    "        # 取出DataLoader中的数据\n",
    "        train_seq = train_seq.to(device)\n",
    "        y_train = y_train.to(device)\n",
    "        # 清空原来的梯度\n",
    "        optimizer.zero_grad()\n",
    "        # 输入到模型\n",
    "        y_train_pred = model(train_seq)\n",
    "        # 获得损失函数的值并且反向传播计算梯度\n",
    "        train_loss = loss_fn(y_train_pred, y_train)\n",
    "        train_loss.backward()\n",
    "        # 使用得到的梯度进行优化\n",
    "        optimizer.step()\n",
    "        train_epoch_loss += train_loss.detach().item()  #每一个批次的损失\n",
    "    # 输出每一轮的损失函数的值\n",
    "    train_epoch_loss /= (train_iter + 1)\n",
    "    print(f'Epoch: {epoch + 1:2} Loss: {train_epoch_loss:10.8f}')\n",
    "    train_all_loss.append(train_epoch_loss)\n",
    "\n",
    "    # 测试步骤开始\n",
    "    model.eval()\n",
    "    valid_loss = None\n",
    "    valid_iter = 0\n",
    "    valid_epoch_loss = 0\n",
    "    with torch.no_grad():\n",
    "        for valid_iter, (valid_seq, y_valid) in enumerate(valid_dataloader):\n",
    "            valid_seq = valid_seq.to(device)\n",
    "            y_valid = y_valid.to(device)\n",
    "            y_valid_pred = model(valid_seq)\n",
    "            valid_loss = loss_fn(y_valid_pred, y_valid)\n",
    "            valid_epoch_loss += valid_loss.detach().item()\n",
    "        valid_epoch_loss /= (valid_iter + 1)\n",
    "        valid_all_loss.append(valid_epoch_loss)\n",
    "\n",
    "# 绘制每一轮的训练和验证损失\n",
    "for i in range(len(train_all_loss)):\n",
    "    writer.add_scalars(\"train_loss\", {'train_loss': train_all_loss[i], 'valid_loss': valid_all_loss[i], }, i)\n",
    "\n",
    "# 保存训练之后的模型参数\n",
    "state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict()}\n",
    "torch.save(state, BILSTM_ATTENTION_PATH)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2024-01-03T05:55:13.285550700Z"
    }
   },
   "id": "87ea765f0832b9aa"
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "outputs": [],
   "source": [
    "def MAE(y_true, y):\n",
    "    return mean_absolute_error(y_true, y)\n",
    "\n",
    "\n",
    "def RMSE(y_true, y):\n",
    "    return np.sqrt(mean_squared_error(y_true, y))\n",
    "\n",
    "\n",
    "def MASE(y_true, y):\n",
    "    return np.mean(np.abs((np.array(y) - np.array(y_true)) / y_true)) * 100\n",
    "\n",
    "\n",
    "def R2(y_true, y):\n",
    "    return r2_score(y_true, y)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:55:57.720080300Z",
     "start_time": "2024-01-03T05:55:57.697072400Z"
    }
   },
   "id": "99758a9517ce75b4"
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MAE= 7.194862319658149\n",
      "RMSE= 17.45580716914033\n",
      "MASE= 7.1261983275615055 %\n",
      "R2= 0.8474787531119002\n"
     ]
    }
   ],
   "source": [
    "scaler = MinMaxScaler(feature_range=(-1, 1))\n",
    "scaler.fit(raw_test_data.reshape(-1, 1))\n",
    "\n",
    "model = BiLSTM_Attention().to(device)\n",
    "model.load_state_dict(torch.load(BILSTM_ATTENTION_PATH)['model'])\n",
    "model.eval()\n",
    "\n",
    "test_pred = []\n",
    "y_all_test = []\n",
    "for seq, y_test in test_dataloader:\n",
    "    seq = seq.to(device)\n",
    "    seq_len = seq.shape[1]\n",
    "    seq = seq.view(model.batch_size, seq_len, 1)\n",
    "    true_y_test = scaler.inverse_transform(np.array(y_test).reshape(-1, 1))\n",
    "    y_all_test.extend(true_y_test)\n",
    "    with torch.no_grad():\n",
    "        y_test_pred = model(seq)\n",
    "        y_test_pred = list(chain.from_iterable(y_test_pred.data.tolist()))\n",
    "        true_predictions = scaler.inverse_transform(np.array(y_test_pred).reshape(-1, 1))\n",
    "        test_pred.extend(true_predictions)\n",
    "\n",
    "print('MAE=', MAE(y_all_test, test_pred))\n",
    "print('RMSE=', RMSE(y_all_test, test_pred))\n",
    "print('MASE=', MASE(y_all_test, test_pred), \"%\")\n",
    "print('R2=', R2(y_all_test, test_pred))\n",
    "\n",
    "# for i in range(100):\n",
    "#     writer.add_scalars(\"BiLSTM-Attention-prediction\", {'true': test_pred[i], 'pred': y_all_test[i], }, i)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-03T05:56:04.038234600Z",
     "start_time": "2024-01-03T05:55:58.595844Z"
    }
   },
   "id": "7e9a1870dcd7ec3"
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "pytorch",
   "language": "python",
   "display_name": "pytorch"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
